import { concatName, FieldEffect, useCall, useEffectState, useFieldName, usePubSub, useSimpleMemo } from '@/components';
import { forkHandler, getToken, isEqual, nextLoop, nextTick } from '@/utils';
import { CODES } from '@/utils/request';
import UploadOutlined from '@ant-design/icons/UploadOutlined';
import { Button, Upload } from 'antd';
import * as PropTypes from 'prop-types';
import React, { memo, useEffect, useState } from 'react';

const defaultRes2Url = (resp) => resp?.data?.[0]?.path ?? null;
const defaultError2Msg = (resp) => resp?.msg;
const defaultRespToName = (resp) => resp?.data?.[0]?.uploadFileName?.replace(/\.[^.]+$/, '') ?? '';
const defaultRespToType = (resp) => resp?.data?.[0]?.uploadFileType ?? null;
const defaultOnResponse = (resp) => {
  if (['100', ...CODES.SUCCESS].includes(resp.code)) return resp;
  throw resp;
};
const defaultElement = <Button icon={<UploadOutlined />}>上传</Button>;

SimpleUpload = memo(SimpleUpload);

/** 附件表单输入组件 */
function SimpleUpload(props) {
  const {
    value: valueProp,
    onChange,
    onResponse,
    respToUrl,
    errorToMsg,
    action = '/UploadAttachmentController/uploadFiles',
    children = defaultElement,
    readOnly,
    headers,
    ...rest
  } = props;
  const [value, setValue] = useEffectState(valueProp ?? null);
  const [fileList, updateFile] = useState([]);
  const currentUrl = fileList?.[0]?.url ?? null;

  useEffect(() => {
    if (!isEqual(valueProp, value) && typeof onChange === 'function') {
      onChange?.(value);
    }
  }, [value]);

  useEffect(() => {
    if (currentUrl === valueProp) return;

    updateFile(
      valueProp
        ? [
            {
              uid: valueProp,
              status: 'done',
              url: valueProp,
              name: valueProp.split(/\//g).pop(),
            },
          ]
        : [],
    );
  }, [valueProp]);

  const handleChange = useCall(
    forkHandler(rest.onUploadChange, async ({ file }) => {
      if (file.status === 'done' && file.response) {
        try {
          let resp = await defaultOnResponse(file.response);
          if (typeof onResponse === 'function') resp = await onResponse(resp);
          const url = (await respToUrl?.(resp)) ?? defaultRes2Url(file.response);

          if (url) {
            await nextLoop();
            return setValue(url);
          }
        } catch (e) {
          const nextFile = { ...file, status: 'error' };
          updateFile([nextFile]);
          nextTick(handleChange, { file: nextFile });
        }
      } else if (file.status === 'error' && file.response) {
        const resp = (await errorToMsg?.(file.response)) ?? defaultError2Msg(file.response);
        updateFile([{ ...file, resp: file.response, response: resp }]);
      } else if (file.status === 'removed') {
        updateFile([]);
        onChange?.(null);
      } else {
        updateFile([file]);
      }
    }),
  );

  const uploadHeaders = useSimpleMemo({ ...(headers || null), token: getToken() });

  return (
    <Upload
      {...rest}
      headers={uploadHeaders}
      action={process.env.BASE_URL + action}
      onChange={handleChange}
      fileList={fileList}
    >
      {!readOnly && children}
    </Upload>
  );
}

SimpleUpload.propTypes = {
  /** 上传请求结束， throw或返回Promise.reject将视为上传失败 */
  onResponse: PropTypes.func,

  /** 从请求结果获取url */
  respToUrl: PropTypes.func,

  /** 从请求结果获取url */
  errorToMsg: PropTypes.func,

  /** 上传文件地址 */
  action: PropTypes.string,

  /** 其他参数继承 Upload */
};

export default SimpleUpload;

/** 通过上传请求结果为文件名和文件类型的表单字段赋值 */
export function NamedUpload(props) {
  const {
    fileNameProp,
    fileTypeProp,
    onResponse,
    respToName = defaultRespToName,
    respToType = defaultRespToType,
    ...rest
  } = props;
  const name = useFieldName();
  const namePubsub = usePubSub();
  const typePubsub = usePubSub();

  const handleResponse = useCall((resp, ...args) => {
    setTimeout(async () => {
      // const fileName = await respToName(resp);
      const fileType = await respToType(resp);
      // namePubsub.update(fileName);
      typePubsub.update(fileType);
    });
    return onResponse?.(resp) ?? resp;
  });

  const handleUploadChange = useCall(
    forkHandler(rest.onUploadChange, ({ file }) => {
      setTimeout(() => {
        namePubsub.update(file.name);
      });
    }),
  );

  const nName = useSimpleMemo(concatName(concatName(name, '../'), fileNameProp));
  const tName = useSimpleMemo(concatName(concatName(name, '../'), fileTypeProp));

  return (
    <>
      {fileNameProp && <FieldEffect name={nName} pubsub={namePubsub} />}
      {fileTypeProp && <FieldEffect name={tName} pubsub={typePubsub} />}
      <SimpleUpload {...rest} onResponse={handleResponse} onUploadChange={handleUploadChange} />
    </>
  );
}

NamedUpload.propTypes = {
  /** 文件名的表单name */
  fileNameProp: PropTypes.any,

  /** 文件类型的表单name */
  fileTypeProp: PropTypes.any,

  /** 从请求结果获取文件名 */
  respToName: PropTypes.func,

  /** 从请求结果获取文件类型 */
  respToType: PropTypes.func,

  /** 其余参数继承SimpleUpload */
};
